# Fastify Fundamentals: How to Validate API Responses

Validation is an important aspect of API development as it helps you check whether an API is working as expected, whether it returns the right data quickly, and if it meets security requirements.

Developers who use Fastify need to remain aware of how to handle validations with the framework.

In this tutorial, we will look at how to validate your API's data and serialize it to be read and stored in a reliable data structure.

## **Fastify and AJV Schema Validator**

Fastify embraces the [JSON schema](https://json-schema.org/), allowing you to define the structure of your API. The JSON schema is used by Fastify for both incoming and outgoing data, allowing it to quickly generate OpenAPI or Swagger specifications from the defined routes.

Our JSON schema validator of choice is [AJV](https://ajv.js.org/), providing a rich API. Validation is important as it checks incoming query strings and body values against the provided schema, which generates an error if it does not match. The error will automatically reach the error handler, which may choose to report the failure to the user.

Let’s look at a short demo to see how this works. In the route folder, create a new file called *movies.js* and paste the code below there:

```javascript
export default function movies (fastify, opts, done) { 
app.post(
    "/movies",
    {
      schema: {
        body: {
          type: "object",
          properties: {
            title: { type: "string" },
            title: { type: "number" },
          },
          required: ["title", "year"],
        },
      },
    },
    async (request, reply) => {
      const { title, year } = request.body;
      return { title, year };
    }, done()
  );
}
```

Then register the plugin in your app.js file as shown below:

```javascript
import fastify from 'fastify'
import movies from './routes/movies.js'


export async function build (opts = {}) {
  const app = fastify(opts)

  //Root route
  app.get('/', async (request, reply) => {
    return { hello: 'world' }
  })

  //Register the movie route here
  app.register(movies)
  
  //Handles request to urls that do not exist on our route
  app.get('/notfound', async (request, reply) => {
    reply.callNotFound()
  })

  //This route gets called if an error occurs within the app and throws an error
  app.get('/error', async (request, reply) => {
    throw new Error('kaboom')
  })
  
  //This handles any error that gets thrown within the application
  app.setErrorHandler(async (err, request, reply) => {
    if (err.validation) {
      reply.code(403)
      return err.message
    }
    request.log.error({ err })
    reply.code(err.statusCode || 500)
  
    return "I'm sorry, there was an error processing your request."
  })
  
  //This handles the logic when a request is made to a route that does not exist
  app.setNotFoundHandler(async (request, reply) => {
    reply.code(404)
    return "I'm sorry, I couldn't find what you were looking for."
  })

  return app
  
}
```

You can proceed to start the server in the terminal. Your terminal should look like this.

![](https://lh7-us.googleusercontent.com/ksXEvjvaph5I9R0_tkbELCzvca9XStqpDcAlwgWO_bjTttGzGz3tO-0rr1Uw29iNU1MeUbfv_VB-CZ1QmicJMOMupPemSTUncOAWdPOqkP_UJBcyghZcZKML5aMmo0rF7TbQcop5gogWksnHnyWi7iM align="left")

Now let’s test our route. We will use a VS code extension called Thunderclient. You can also use Postman, Rest Client or any other API testing VS Code extensions.

In the URL field, paste the movie route [http://localhost:3000/movies](http://localhost:3000/movies) and set the request type to a POST request.

Then, click on the Body in the tabs area and select JSON.

First, let's check the response type when we input the correct datatypes as specified in our API.

![](https://lh7-us.googleusercontent.com/KIsdFtEGZ-YWXbo8HyloGUzecDDEspGhVxFn5cGwY1R17fD0f9yK4MZmIPT0Zej_d1tXE4MxNnpUTg5i6e7ouECjtxKy9F-X_9Kr6rDole-1Gr_pSi_ciO_a2uY6Ose_LbEYp1bcgQo6-Jotk2EBgJY align="left")

We get a status code of 200 and the appropriate response as specified in our API.

Now, let's send another request, but this time, we will pass in a string as the value for the year property.

![](https://lh7-us.googleusercontent.com/CYB8thVpQHXRR5rPGXBK15Ewz5_3PqVILjEVk40HoKdRqvkbOdhjzkmkHWkMFaxwxkfQ_jhq5T83aNj9YRFLkO0ls-LzkVLoM742abt2NqgLwU1lEIDGIesIFi1KvBgeDsRGYg9qwX5G7CL0pb4nA0Q align="left")

This time we get a status code of 403 and a response stating that body/year must be a number. This error message is automatically handled by the JSON Schema validator in our app.

Finally, let's send another request without the title property in our request body.

![](https://lh7-us.googleusercontent.com/GKiTp1ynlAAO-uk9ZK1mksugZDB_xvJ-WYt-JjSOLS_-VtcNPc8L0XpFvqO1obhFbDWn29ztyX_jBS9f1HbxChn98xxMacdwcwg7e8el6CfoYLvue3rZPH49FsBCmlX5PUmp5PfUfDODX8UwSy57vTA align="left")

Here we also get a response status 403 and a response message stating that the 'body must have required property “title”'.

```javascript
import movies from './routes/movies.js'

//some code here

app.register(movies)
```

When you run the code in the terminal (using test data as shown below), here is what happens:

```plaintext
curl -X POST -H 'Content-Type: application/json' -d '{"title" : "foo", "year": 2024 }' http://localhost:3000/movies
```

## **Serializations**

Fastify provides a serialization solution, making it possible to specify a custom schema for each status code range that the routes return.

While this response may not be completely validated against the schema, unwanted properties are filtered out, helping to increase the application's performance.

This is because [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) allows the rendering of JSON faster than [JSON.stringify()](https://www.w3schools.com/js/js_json_stringify.asp). It uses the same syntax as JSON Schema.

## **TypeBox**

Typing untrusted input without validation is a security hazard. If you desire type-safety, you can use a tool like TypeBox.

TypeBox is a JSON Schema type builder and runtime validator for TypeScript. It allows you to define the expected structure of JSON data using familiar TypeScript types and then validate incoming data against those predefined types at runtime.

Here is an example of how to implement TypeBox in your Fastify application:

```javascript
import fastify, { FastifyServerOptions } from 'fastify'
import { Type, TypeBoxTypeProvider } from '@fastify/type-provider-typebox'

//create an instance for the Fastify application
const app = fastify.withTypeProvider<TypeBoxTypeProvider>();

app.get('/search', {
  schema: {
    querystring: Type.Object({
      q: Type.Required(Type.String({ minLength: 3 }))
    })
  }
}, async function (request) {
  return { q: request.query.q } 
})

const movie = Type.Object({
  title: Type.Required(Type.String()),
  year: Type.Required(Type.Number())
})

app.post('/movies', {
  schema: {
    body: movie,
    response: {
      200: movie
    }
  }
}, async function (request) {
  return { title: request.body.title, year: request.body.year' }
})


app.listen({ port: 3000 })
```

In this video, we took a look at this in further depth, exploring how to write type-safe Fastify applications.

%[https://www.youtube.com/watch?v=hx6jy3MzQzw] 

## **Security Notice**

It is important to note that schema definitions are part of the application code. Hence, if schemas are obtained from untrusted sources, the application is at risk of injection attacks.

Additionally, the ***$async*** Ajv feature, which is disabled by default, should not be used as part of the first validation strategy. The option is used to access databases and reading them during the validation process may lead to a Denial of Service attack on the application.

Instead, if you need to run asynchronous tasks, use Fastify’s hooks after validation completes such as ***preHandler.***

## **Wrapping Up**

In this tutorial, we’ve explored how to use the AJV Schema validator in your Fastify applications and how to handle subsequent errors.

In addition, you should now be able to  specify a custom schema that your API returns by implementing serialization. Lastly, we delved into how to build type-safe validations with tools such as Typebox.

## **Supercharging Fastify Development with Platformatic**

Developed by the co-creator of Fastify, Platformatic is a backend development platform designed to extend the capabilities of the Fastify web framework. Together, [Platformatic](http://platformatic.dev) and Fastify offer:

* Developer-centric design
    
* Real-time metrics
    
* A vast plugin ecosystem
    
* Built-in validation and serialization
    
* Built-in logging
    

[Find out more and get in touch.](https://platformatic.dev/feature/fastify/)
