Refined Code: Structuring REST API

Key notes on making your REST API accessible and maintainable.

Photo by Nubelson Fernandes on Unsplash

Implementing a resourceful API is one of the most significant part of any software development process as it shoulders the responsibility of data communication between applications and therefore a lot depends on its reliability, security, latency and scalability. The crucial nature of its working requires one to write the code in way that it is quite maintainable and accessible to fellow developers who work with you or who will work after you.

Here are some key points that will help you structure your API code in a better way.

1. Folder structure

I cannot stress enough about the importance of writing modular code in a shared workspace.

  • Separate your functionality in separate folders starting with routes (API endpoints and routers), controllers (service that talk to the database once an endpoint is called) and entities/models (repository of the database). Inside the folder, have separate files for each target.
  • Have a separate folder for all your middleware functions.
  • Put all the utility functions in a utils folder and configuration options in config folder.
  • Define your constants in a new folder. (More about it later in this blog)
  • New folder for your DTO classes or interfaces and for types if you use TypeScript.
src
├───config
│ connection.json
├───constants
│ index.ts
├───controllers
│ Posts.controller.ts
│ Users.controller.ts
├───entity
│ Posts.entity.ts
│ Users.entity.ts
├───middleware
│ auth.ts
├───routes
│ index.ts
│ Posts.route.ts
│ Users.route.ts
└───types
index.ts

Identify modular code in your project and maintain a locatable folder structure is the first step to writing clean code.

2. HTTP methods and related parameters

HTTP request methods, also known as HTTP verbs, are well crafted to describe the type of request you send. Always pick the correct method.

  • GET request when fetching data.
  • POST request when sending data.
  • PUT request when updating the entire data object, i.e., when performing a full update.
  • PATCH request when updating a part of data object, i.e., when performing a partial update.
  • DELETE request when deleting data.

Also make sure not to expose private information like tokens or API keys in query parameters and instead send them as part of the header field. Even while sending data over POST request in form of request body, make sure to encrypt important data like passwords.

3. HTTP status codes and response object

HTTP status codes are a nifty way of describing the response you send in form of a JSON object. Here are some frequently used:

  • 200 success OK response for successful operations.
  • 201 created response for when operations like register are successful.
  • 401 authentication error when login credentials are invalid.
  • 403 authorization error when user does not a token or permission to access a certain route.
  • 404 resource not found error when request data does not exist on server side.
  • 500 generic server side error for failed operations.

Prefix your res.send() with res.status(XXX).send() and include relevant status code when sending a response. Although status codes are automatically inferred by the server it is always a good practice to include them yourselves.

Another good practice is to define a generic response object. Here’s a simple response:

// for success
return res.status(200).send({
success: true,
data: "your data here",
error: null,
})
// for error
return res.status(500).send({
success: false,
data: null,
error: "Server Error",
})

4. Error handling

Wrap your business logic in try…catch blocks. This is a simple yet important task to manage. This ensures that if your transaction fails you can always perform rollback operations and send appropriate response message.

try {
await Users.save({
username, email, password
})
return res.status(201).send({
success: true,
data: "Account successfully created.",
error: null,
})
} catch (err) {
return res.status(500).send({
success: false,
data: null,
error: {
message: 'Server Error',
},
})
}

5. Constants at play

Earlier I mentioned creating a folder for constants. An implementation of DRY — don’t repeat yourself — principle. There are a lot of things which are repeated when scripting a REST API. For example, the name of routes belonging to a single collection, i.e., all user routes will have /api/v1/users/ prefixed. Imagine change the /v1/ to /v2/ in a newer development cycle. Had you declared your USER_ROUTER as a constant value, all you will have to change is it’s value at one place. Otherwise, you will have to change every occurrence across multiple files.

There is a lot repetition that can happen. Scrutinize your code for repetitive patterns and make use of constants and environment variables (if the constant should not be exposed). This will save you a ton of work in case things go sideways.

Bonus

I would advise commenting your REST API controller endpoints. You can use descriptors as follows:

/**
* @route PUT /posts/:id
* @desc fetch a post using given id
*/
exports.getPostById = async (req: Request, res: Response) => {
try{ //code here }
catch(err){ // code here }
}

Furthermore, you can learn how to write tests for your API to solidify your business logic. However, please know that testing is a different requirement and a different skill. It is surely used at industrial level. You can try exploring the domain if you find yourselves inclined to it.

That’s all the basics you need to know to write your APIs in a structured manner making it more readable, accessible and maintainable. Writing refined code is industry best practice and every developer must aim to be concise. Thank you for reading. I hope you implement the points shared and strive to become a better dev.

Currently working as a software engineering intern at Websmith Solution.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store