30
loading...
This website collects cookies to deliver better user experience
npm init -y
in a terminal, this will create a file called package.json with some information about your app in JSON.npm install Fastify
command to install Fastify. Other packages we will be installing arenpm install -D nodemon
.npm install config
main
to server.js
, because the file we will create our server in is going to be called server.js. Furthermore, delete the test
property and value. Paste the following code inside the script
property."start": "node server.js",
"server": "nodemon server.js"
npm start
on the terminal, it will run our server.js file which will be created soon. But when we run the command npm run server
on the terminal, it will run our server.js file using nodemon.const fastify = require('fastify')({ logger: true });
logger: true;
key value is an option for activating logging on our terminal from Fastify. So the information of requests, server starting, response, errors will all be logged in the terminal.PORT
variable, I will use 5000 for mine. Why we create a variable for it is for the sake of deploying to production. So you should have something like const PORT = process.env.PORT || 5000
. As such we are either using the port of the host company (like Heroku or digital ocean) or our customized 5000./
.fastify.get('/', (req, reply) => {
reply.send('Hello World!');
});
req
and reply
stands for request and reply (response). They are parameters obviously, so you can call it whatever you like. But we would go with this simple and readable form.fastify.listen(port)
to listen for requests to our server. But this function returns a promise, so we would create a function that handles this promise using async and await.const startServer = async () => {
try {
await fastify.listen(PORT);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
startServer()
and run npm run server
on the terminal to get the server started.http://localhost:5000
. Use any API testing tool of your choice to test and you should get a Hello world message as a response.startServer
function.fastify.register(require('./routes/posts')); // we will be working with posts.js only for now
register
function, choice is yours.postRoutes
and pass these three parameters fastify, options, and done. This function is going to make an instance of our fastify server, which means with the first parameter we can do everything we could do in server.js with the fastify
variable.postRoutes
function in posts.js.postRoutes
should look like this:const postRoutes = (fastify, options, done) => {
fastify.get('/', (req, reply) => {
reply.send('Hello world');
});
};
postRoutes
function, to indicate we are done. Just like making a middleware in Express and calling next to move on.done()
at the last line of the postRoutes
function.module.exports = postRoutes
.fastify.get('/', (req, reply) => {
reply.send([
{ id: 1, title: 'Post One', body: 'This is post one' },
{ id: 2, title: 'Post Two', body: 'This is post two' },
{ id: 3, title: 'Post Three', body: 'This is post three' },
]);
});
schema
property.const opts = {
schema: {},
};
const postRoutes = (fastify, options, done) => {
fastify.get('/', opts);
done();
};
schema
: defines how our data should be set up, what data should come in, and what data should go out, including their types (string, boolean, number, etc).
preHandler
: a function that defines what should be done before requests are handled by the handler
below.
handler
: a function that handles the request.
preHandler
is going to be used for authentication, which means it will be used on protected routes only.const opts = {
schema: {
response: {
200: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'number' },
title: { type: 'string' },
body: { type: 'string' },
},
},
},
},
},
handler: (req, reply) => {
reply.send([
{ id: 1, title: 'Post One', body: 'This is post one' },
{ id: 2, title: 'Post Two', body: 'This is post two' },
{ id: 3, title: 'Post Three', body: 'This is post three' },
]);
},
};
id
, title
, and body
which are of type number
, string
, and string
respectively.response
, 200
, type
. The items
and properties
can be any name but I recommend using these names.id
property and value from the schema object you would notice the id
property is no longer sent as part of the response. While if you try changing the id
property from type number
to type string
, you would see it as a string in the response. Cool right!getPostsOpts
.const getPostsOpts = {
schema: {
response: {
200: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'number' },
title: { type: 'string' },
body: { type: 'string' },
},
},
},
},
},
handler: (req, reply) => {
reply.send([
{ id: 1, title: 'Post One', body: 'This is post one' },
{ id: 2, title: 'Post Two', body: 'This is post two' },
{ id: 3, title: 'Post Three', body: 'This is post three' },
]);
},
};
const postRoutes = (fastify, options, done) => {
fastify.get('/', getPostsOpts);
done();
};
getPostsSchema
and cut the value of the schema
property (from routes/posts.js) and paste it as the object. Your code should look like thisconst getPostsSchema = {
response: {
200: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'number' },
title: { type: 'string' },
body: { type: 'string' },
},
},
},
},
};
const getPostsSchema = {
// our schemas
};
module.exports = { getPostsSchema };
schema
property.const { getPostsSchema } = require('../controllers/schemas/posts.js');
const getPostsOpts = {
schema: getPostsSchema,
handler: (req, reply) => {
reply.send([
{ id: 1, title: 'Post One', body: 'This is post one' },
{ id: 2, title: 'Post Two', body: 'This is post two' },
{ id: 3, title: 'Post Three', body: 'This is post three' },
]);
},
};
getPostsHandler
with req
and reply
as our params. Copy the function body from the routes/posts.js file and paste it here, after which export the function. It should look like thisconst getPostsHandler = (req, reply) => {
reply.send([
{ id: 1, title: 'Post One', body: 'This is post one' },
{ id: 2, title: 'Post Two', body: 'This is post two' },
{ id: 3, title: 'Post Three', body: 'This is post three' },
]);
};
module.exports = { getPostsHandler };
getPostsHandler
into the routes/posts.js file, and set it as the value of the handler method. Your routes/posts.js would look like thisconst { getPostsSchema } = require('../controllers/schemas/posts.js');
const { getPostsHandler } = require('../controllers/handlers/posts.js');
const getPostsOpts = {
schema: getPostsSchema,
handler: getPostsHandler,
};
const postRoutes = (fastify, opts, done) => {
fastify.get('/', getPostsOpts);
done();
};
fastify.get('/api/posts', getPostsOpts);
const posts = [
{ id: 1, title: 'Post One', body: 'This is post one' },
{ id: 2, title: 'Post Two', body: 'This is post two' },
{ id: 3, title: 'Post Three', body: 'This is post three' }, // you can add as many as you want
];
module.exports = posts;
send
function i.econst posts = require('../../cloud/posts.js');
const getPostsHandler = (req, reply) => {
reply.send(posts);
};
module.exports = { getPostsHandler };
http://localhost:your_port/api/posts
id
as a parameter from the request and we will filter the posts
array to find that post.fastify.get('/api/posts/:id', getPostOpts); // the :id route is a placeholder for an id (indicates a parameter)
getPostOpts
objectconst getPostOpts = {
schema: getPostSchema, // will be created in schemas/posts.js
handler: getPostHandler, // will be created in handlers/posts.js
};
getPostSchema
and paste the followingconst getPostSchema = {
params: {
id: { type: 'number' },
},
response: {
200: {
type: 'object',
properties: {
id: { type: 'number' },
title: { type: 'string' },
body: { type: 'string' },
},
},
},
};
getPostSchema
, simply add it to the object being exported i.e module.exports = { getPostsSchema, getPostSchema };
const typeString = { type: 'string' }; // since i will be using this type a lot
const post = {
type: 'object',
properties: {
id: { type: 'number' },
title: typeString,
body: typeString,
},
};
const getPostsSchema = {
response: {
200: {
type: 'array',
items: post,
},
},
};
const getPostSchema = {
params: {
id: { type: 'number' },
},
response: {
200: post,
},
};
module.exports = { getPostsSchema, getPostSchema };
getPostHandler
and paste the followingconst getPostHandler = (req, reply) => {
const { id } = req.params;
const post = posts.filter((post) => {
return post.id === id;
})[0];
if (!post) {
return reply.status(404).send({
errorMsg: 'Post not found',
});
}
return reply.send(post);
};
http://localhost:5000/api/posts/4
will return 4 as its id.reply.status
function tells Fastify what status code the response should be. If the post is not found a customized error message is sent, with Fastify we could also usereturn reply.status(404).send(new Error('Post not found'));
{
"statusCode": 404,
"error": "Not Found",
"message": "Post not found"
}
getPostHandler
and save all files. Run the program and test your new route.postRoutes
function. Just after the last route we created, paste the code belowfastify.post('/api/posts/new', addPostOpts);
/api/posts/new
is our endpoint to add a new post to our array of posts. The next thing we'd do is create the addPostOpts
object outside of our routes function and pass a value for schema and handlerconst addPostOpts = {
schema: addPostSchema, // will be created in schemas/posts.js
handler: addPostHandler, // will be created in handlers/posts.js
};
preHandler
to the object above in the next article.addPostSchema
, assign the code below to it;const addPostSchema = {
body: {
type: 'object',
required: ['title', 'body']
properties: {
title: typeString, // recall we created typeString earlier
body: typeString,
},
},
response: {
200: typeString, // sending a simple message as string
},
};
body
as a property to tell Fastify what to expect from the request body of our post route. Just like we did with params
above. We can also do the same for headers
(I will show you this during authentication).required
property we are telling Fastify to return an error if both title
and body
are not part of the request body.addPostSchema
to the object being exported out of this file (schemas/posts.js).const addPostHandler = (req, reply) => {
const { title, body } = req.body; // no body parser required for this to work
const id = posts.length + 1; // posts is imported from cloud/posts.js
posts.push({ id, title, body });
reply.send('Post added');
};
addPostHandler
to the object being exported out of this file (handlers/posts.js).addPostSchema
and addPostHandler
to the object being imported into routes/posts.js.http://localhost:your_port/api/posts
(our first endpoint), you would see it at the bottom of the array.put
method for this route. Add the code below to your postRoutes
functionfastify.put('/api/posts/edit/:id', updatePostOpts);
updatePostOpts
object outside of the postRoutes
function. As before, we will pass a value for the schema
and handler
properties i.econst updatePostOpts = {
schema: updatePostSchema, // will be created in schemas/posts.js
handler: updatePostHandler, // will be created in handlers/posts.js
};
updatePostSchema
and updatePostHandler
to the imported objects in this file (routes/posts.js).updatePostSchema
and use this code for itconst updatePostSchema = {
body: {
type: 'object',
required: ['title', 'body'],
properties: {
title: typeString,
body: typeString,
},
},
params: {
id: { type: 'number' }, // converts the id param to number
},
response: {
200: typeString, // a simple message will be sent
},
};
updatePostSchema
to the object being exported out.const updatePostHandler = (req, reply) => {
const { title, body } = req.body;
const { id } = req.params;
const post = posts.filter((post) => {
return post.id === id;
})[0];
if (!post) {
return reply.status(404).send(new Error("Post doesn't exist"));
}
post.title = title;
post.body = body;
return reply.send('Post updated');
};
updatePostHandler
to the object being exported out.fastify.delete('/api/posts/:id', deletePostOpts);
deletePostOpts
object would beconst deletePostOpts = {
schema: deletePostSchema,
handler: deletePostHandler,
};
const deletePostSchema = {
params: {
id: { type: 'number' }, // converts the id param to number
},
response: {
200: typeString,
},
};
const deletePostHandler = (req, reply) => {
const { id } = req.params;
const postIndex = posts.findIndex((post) => {
return post.id === id;
});
if (postIndex === -1) {
return reply.status(404).send(new Error("Post doesn't exist"));
}
posts.splice(postIndex, 1);
return reply.send('Post deleted');
};