Home >Web Front-end >JS Tutorial >[Roast: Day - Auto-Create an API Server Without AI
To connect my front end interface to the database, I need to design an API to allow the frontend application to retrieve and send data. This will enable my user logic, like creating accounts, signing in and out, etc. It will enable all the logic around roasts.
I've created an Express API before, but this time I wanted to see if I could learn a bit more about Swagger in the build process.
Swagger is a company that designed and maintains three products, the Swagger Editor, Swagger CodeGen and Swagger UI. These three products work hand in hand to make creating an OpenAPI compliant application (and the documentation) easier!
The process of using Swagger to create an API starts with the Swagger Editor. This tool allows you to create what is called a contract. The contract is a YAML document that defines different things about the application like the name, routes that it will handle and so forth.
If you haven't worked with YAML before, it's a looser object-based markup language that has some similarity with JSON, but it's much easier to type up quickly. Here's an example of the same content in JSON and then in YAML:
// JSON Example { "name": "ThisApp", "description": "An example of data.", "list": [ "option1", "option2", "option3" ], "object": { "key": "value" } }
# YAML Example name: ThisApp description: An example of data. list: - option1 - option2 - option3 object: key: value
Notice that YAML, while still machine-readable, is a bit easier for humans to read too, making it a great markup language for this specification.
Using the Swagger Editor and following OAS, you can write out everything that you would ordinarily program into an API.
At the top-level, you will define the specifics of the application:
openapi: 3.0.3 info: title: Roast - Know Your Home Roasts description: # This will appear in the API UI version: 1.0.0 servers: - url: # A url to one or more servers here tags: These are groups of paths - name: user description: Operations for your user - name: roast description: Access to roasts paths: # Define all of your paths in this object components: # Define reusable pieces of code here
The magic of the CodeEditor comes to life when you begin defining paths. Take the following path that I defined to get a single roast by id.
# ... contract information paths: # ... users paths, etc. /roasts/{roastId}: get: tags: - roast summary: Get roast by id description: Must include a valid roast id parameters: - $ref: '#/components/parameters/roastIdParam' responses: '200': description: Successful operation content: application/json: schema: $ref: "#/components/schemas/Roast" '400': $ref: '#/components/responses/Invalid' '404': $ref: '#/components/responses/NotFound'
Let's break down this object. First, it is called /roasts/{roastId}. This means that we are defining the expected behavior of the server when a request is sent to this route. What happens below that?
Developing a contract like this, you will likely find yourself typing the same things over and over. And you may know as a programmer, that we want to follow the DRY principle: "Don't Repeat Yourself." For example, when defining a required request body, there will be several endpoints that may require the same object.
Well here's where components come in. For this example, you can define a schema and then reference that schema anywhere in the contract using $ref: .
So, at the bottom of my contract, I have a components: object.
components: schemas: Roast: # An arbitrary name to identify the schema type: object properties: id: type: integer format: int64 example: 10 ## Include all other properties
This component object, contains a schema called Roast , so now, if I needed to specify that this object needed to be sent in a request, say, in a POST request to /roast to add a new roast. I can reference the object this way:
/roast: post: # additional specifications requestBody: content: application/json: schema: $ref: '#/components/schemas/Roast'
You can also do this in defining your parameters and many other repeated sections of your spec!
While you're typing away in your Swagger Editor, all the while, the Swagger UI is being constantly updated in the window to your right. Swagger UI is updating what will become your API documentation! This means you don't have to go back later and write out the documentation on your own.
The best part about this, is that it will be served alongside your application (so long as you use CodeGen to create it).
Once you feel like your API is up to spec, you can have a working server in 17 different languages/frameworks in seconds! Just click Generate Server, and select your flavor and CodeGen reads your OAS spec and downloads a server.
In Node, your code comes out in a few different directories.
generated-server/ |-- api/ |-- controllers/ |-- service/ |-- utils/ |-- README.md |-- index.js |-- package.json
The api directory contains your OpenAPI spec. The controllers directory contains a file for each of your path groups, with exported functions specifically for handling the unique paths and operations of your application. The service directory, is where you will hook these operations up to your database and perform the business logic of the application, and utils contains functions that help read and write data.
Your server will actually live in index.js.
Yes! Well, sort of.
CodeGen does in fact make a working server for you, but it's still up to you to hook up the database and design the logic of the application! But, it gives you a complete and organized skeleton that you can work in!
Here's an example of the code that it output for my POST request to /roasts .
// controllers/Roast.js // ... module.exports.addRoast = function addRoast (req, res, next, body) { Roast.addRoast(body) .then(function (response) { utils.writeJson(res, response); }) .catch(function (response) { utils.writeJson(res, response); }); }; // service/RoastService.js // ... exports.addRoast = function(body) { return new Promise(function(resolve, reject) { var examples = {}; examples['application/json'] = { "notes" : "Doesn't taste as good as last time... I wonder if the weather is making the beans roast faster now that it's warmer", "heatLevel" : "Med", "origin" : "Ethiopian", // ... "id" : 10, }; if (Object.keys(examples).length > 0) { resolve(examples[Object.keys(examples)[0]]); } else { resolve(); } }); }
The above code was entirely generated by Swagger CodeGen. However, it won't actually add a roast object anywhere. This is creating a mock object and sending it back. But, after hooking up a Postgres database, and creating queries with this logic inside the service, the API will be fully operational!
I loved working with Swagger on this API, it was the first time that I committed to learning some of the intricacies of OAS to generate the server. The only rub that I have with it is that the docs are not formatted the best, and they can be hard to navigate searching for what you want to add to the server.
But, once you're familiar with OAS, this tool can save a ton of time. Not only do you have extremely thorough documentation when you're done, but you can treat the generated code as a to-do list of the functions and logic that you need to implement as you build!
Would you give it a try?
If you want to keep up with the changes, fork and run locally, or even suggest code changes, here’s a link to the GitHub repo!
https://github.com/nmiller15/roast
The frontend application is currently deployed on Netlify! If you want to mess around with some features and see it in action, view it on a mobile device below.
https://knowyourhomeroast.netlify.app
Note: This deployment has no backend api, so accounts and roasts are not actually saved anywhere between sessions.
The above is the detailed content of [Roast: Day - Auto-Create an API Server Without AI. For more information, please follow other related articles on the PHP Chinese website!