diff --git a/Annotation/ApiDoc.php b/Annotation/ApiDoc.php
new file mode 100644
index 000000000..006a40e48
--- /dev/null
+++ b/Annotation/ApiDoc.php
@@ -0,0 +1,61 @@
+formType = $data['formType'];
+ } else if (isset($data['filters'])) {
+ foreach ($data['filters'] as $filter) {
+ if (!isset($filter['name'])) {
+ throw new \InvalidArgumentException('A "filter" element has to contain a "name" attribute');
+ }
+
+ $name = $filter['name'];
+ unset($filter['name']);
+
+ $this->filters[$name] = $filter;
+ }
+ }
+
+ if (isset($data['comment'])) {
+ $this->comment = $data['comment'];
+ }
+ }
+
+ public function getFilters()
+ {
+ return $this->filters;
+ }
+
+ public function getFormType()
+ {
+ return $this->formType;
+ }
+
+ public function getComment()
+ {
+ return $this->comment;
+ }
+}
diff --git a/DependencyInjection/NelmioApiExtension.php b/DependencyInjection/NelmioApiExtension.php
new file mode 100644
index 000000000..15b249a29
--- /dev/null
+++ b/DependencyInjection/NelmioApiExtension.php
@@ -0,0 +1,21 @@
+load('request_listener.xml');
+ $loader->load('services.xml');
+ }
+}
diff --git a/EventListener/RequestListener.php b/EventListener/RequestListener.php
new file mode 100644
index 000000000..fa73d12ae
--- /dev/null
+++ b/EventListener/RequestListener.php
@@ -0,0 +1,57 @@
+reader = $reader;
+ $this->router = $router;
+ $this->formatter = $formatter;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onKernelRequest(GetResponseEvent $event)
+ {
+ if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
+ return;
+ }
+
+ $request = $event->getRequest();
+
+ if (!$request->get('_doc')) {
+ return;
+ }
+
+ preg_match('#(.+)::([\w]+)#', $request->get('_controller'), $matches);
+ $method = new \ReflectionMethod($matches[1], $matches[2]);
+ $route = $request->get('_route');
+
+ if ($annot = $this->reader->getMethodAnnotation($method, $this->annotationClass)) {
+ if ($route = $this->router->getRouteCollection()->get($route)) {
+ $result = $this->formatter->format($annot, $route);
+
+ $event->setResponse(new JsonResponse($result));
+ }
+ }
+ }
+}
diff --git a/Formatter/ApiDocFormatter.php b/Formatter/ApiDocFormatter.php
new file mode 100644
index 000000000..d44fbe517
--- /dev/null
+++ b/Formatter/ApiDocFormatter.php
@@ -0,0 +1,53 @@
+parser = $parser;
+ }
+
+ public function format(ApiDoc $apiDoc, Route $route)
+ {
+ $method = $route->getRequirement('_method');
+ $data = array(
+ 'method' => $method,
+ 'uri' => $route->compile()->getPattern(),
+ 'requirements' => $route->compile()->getRequirements(),
+ );
+
+ unset($data['requirements']['_method']);
+
+ if (null !== $formType = $apiDoc->getFormType()) {
+ $data['parameters'] = $this->parser->parse(new $formType());
+
+ if ('PUT' === $method) {
+ // All parameters are optional with PUT (update)
+ array_walk($data['parameters'], function($val, $key) use (&$data) {
+ $data['parameters'][$key]['is_required'] = false;
+ });
+ }
+ }
+
+ if ($filters = $apiDoc->getFilters()) {
+ $data['filters'] = $filters;
+ }
+
+ if ($comment = $apiDoc->getComment()) {
+ $data['comment'] = $comment;
+ }
+
+ return $data;
+ }
+}
diff --git a/NelmioApiBundle.php b/NelmioApiBundle.php
new file mode 100644
index 000000000..1791149ed
--- /dev/null
+++ b/NelmioApiBundle.php
@@ -0,0 +1,9 @@
+ 'string',
+ 'date' => 'date',
+ 'datetime' => 'datetime',
+ 'checkbox' => 'boolean',
+ 'time' => 'time',
+ 'number' => 'float',
+ 'integer' => 'int',
+ 'textarea' => 'string',
+ );
+
+ public function __construct(FormFactoryInterface $formFactory)
+ {
+ $this->formFactory = $formFactory;
+ }
+
+ public function parse(AbstractType $type)
+ {
+ $builder = $this->formFactory->createBuilder($type);
+
+ $parameters = array();
+ foreach ($builder->all() as $name => $child) {
+ $b = $builder->create($name, $child['type'], $child['options']);
+
+ $bestType = '';
+ foreach ($b->getTypes() as $type) {
+ if (isset($this->mapTypes[$type->getName()])) {
+ $bestType = $this->mapTypes[$type->getName()];
+ }
+ }
+
+ $parameters[] = array(
+ 'name' => $name,
+ 'type' => $bestType,
+ 'is_required' => $b->getRequired()
+ );
+ }
+
+ return $parameters;
+ }
+}
diff --git a/Resources/config/request_listener.xml b/Resources/config/request_listener.xml
new file mode 100644
index 000000000..26279e627
--- /dev/null
+++ b/Resources/config/request_listener.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ Nelmio\ApiBundle\EventListener\RequestListener
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
new file mode 100644
index 000000000..370b52ebc
--- /dev/null
+++ b/Resources/config/services.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ Nelmio\ApiBundle\Parser\FormTypeParser
+ Nelmio\ApiBundle\Formatter\ApiDocFormatter
+
+
+
+
+
+
+
+
+
+
+
+