For us all to be on the same page.
-
server: Refers to the server instance. -
client: The user requesting information from server. -
HTTP request/response: Request or Response that is in the form as specified by the HTTP specification. -
request: Refers to the HTTP request sent to the server by the client. -
response: Refers to the HTTP response sent by the server to the client in response to the request. -
path: A unique URL in the domain of the server. eg. If server is running on 'helloworld.com' then in the URLhelloworld.com/abc/def,/abc/defis the path -
method: Refers to one of the HTTP Methods defined by the HTTP Spec. A single route can have multiple methods, eg.GET /helloandPOST /hello. Both the methods are supposed to perform different tasks, and which method is used to serve the request based on the method specified in the request. -
HTTP method: Some methods that are defined in the HTTP specification. -
route: Refers to one of the configured paths where the server will listen for HTTP Requests for a specific method. -
router: A group of a bunch of routes based on their paths. eg. Both the routes forGET /hello/worldandGET /hello/byewill have the common router '/hello' but the router/hello/worldor/hello/byeis unique to both the routes respectively. -
query: It is some additional information provided in the url for a specific route without sending it in the body. eg. In the URLhelloworld.com/abc/def?hi=bye&foo=bar, the path is still/abc/def, and the queries arehiandfoo, with valuesbyeandbarrespectively. -
param: It refers to the variable part of the URL that maybe used to point to a specific resource eg. In the URLgithub.com/resyfer, the variable part isresyfer, which basically refers to the username of the person who's profile we want to view. So it would be written asgithub.com/:usernameand thus the param would beusernamewith valueresyferin this case. -
base: Refers to the directory relative to which you want to give paths for files to serve. eg. If I have files/home/resyfer/hello/a.htmland/home/resyfer/hello/b.css, then I can set the base (shown later) as/home/resyfer/helloand provide the paths to the file as/a.htmland/b.css. -
controller: A user-defined function that performs a task given the request and sends the response. -
middleware: A user-defined function that performs a task given the request and then transfers the control to another middleware or a controller. -
port: A unique number used by a process accessing the network for identification and routing the request to the specific process by the operating system. If we say that port 3000 is free, it means 3000 is a port that is not being currently used by a process. If we say server is listening on port 3000, it means that the port number 3000 will be used by the operating system to identify the server process and thus route any requests to port 3000 to the server.
#include <libexpress/express.h>Assuming you're writing your code in a file, say a.c, then:
$ gcc a.c -o a.o -lexpress -lcol -lasync -ltpool -lqueue -lhmap -lvectorserver_t *app = server_new();Now app is your server (specifically, your server instance).
You can make the server listen for HTTP requests using:
server_listen(app, 3000);Where 3000 is an example for a port number. Any port that is free will do of course.
Now, just initializing the server and listening for port won't do anything useful. It doesn't know what to do when a client makes a HTTP request.
So before you listen to the server (and after initializing the server) you need to configure your routes.
Four of the most common HTTP methods have been simplified for you to configure, as they represent the basic Create-Read-Update-Delete (CRUD) operations.
route_get(app, "/rest/api/route", middleware1, middleware2, ..., MID_END, controller); // GET method
route_post(app, "/rest/api/route", middleware1, middleware2, ..., MID_END, controller); // POST method
route_put(app, "/rest/api/route", middleware1, middleware2, ..., MID_END, controller); // PUT method
route_delete(app, "/rest/api/route", middleware1, middleware2, ..., MID_END, controller); // DELETE methodFirst, it takes the router. Now, it can be the topmost router (which is the server), or it can take a child router (will be explained below).
Second, it takes in the path. Now, the route is relative to the path of the router
that you gave. If the router is the server (ie. path is /) then giving a path
as /rest/api/route ultimately gives you a route for /rest/api/route. If your
router has a path, say, /rest, then giving the path /api/route will utlimately
give you the route for /rest/api/route.
Third, it takes in any number of middlewares. Middlewares have the following syntax:
void name(req_t *req, res_t *res) {
}Where the pointer req is of type req_t and res of type res_t, and they
are the request and response objects respectively (more on that later).
After you return from the function, the control is shifted to the next middleware or controller in route.
Fourth, the second last parameter is a macro named MID_END that signifies the
end of middlewares, and after that the controller is used.
Fifth, the last parameter a route may take is a controller. Now, controllers are no different from middlewares except in the name and essence. A controller is that handler which finally sends a response to the client (shown later). Do note that a middleware may send a response as well if it wants, but controllers have to send a response before it returns (or client will not get any response).
Remember, it is not necessary for a route to have a controller (you can give a NULL). However it is necessary for a route that the user is requesting to end with a controller. Sounds confusing? Let me elaborate:
Suppose you have 2 routes /hello and /hello/world. Accessing /hello/world
will lead to middlewares of both the routes being executed in sequential
order, so middlewares of /hello and /hello/world both will be executed
sequentially. This means that, assuming the client only requests /hello/world,
then /hello need not have a controller as long as /hello/world has one.
That's all there is to configuring routes.
There's another way to configure a route, one that lets you use any HTTP method you like, or even custom ones of your own (like the suprisingly popular "PATCH" method):
route(app, "/rest/api/route", "method", middleware1, middleware2, ..., MID_END, controller);A special method * can also be used, which is a method that matches any method
given that the method in the request is not matched by any other configured
method, as well as assuming that a route with * method was configured.
NOTE: Please use * in the path for configuring routers in /hello/* form only. /hello/wor* won't work, it will try to exactly match wor* and /hello/*/world will match be equivalent to /hello/*.
A path can be of various forms:
/hello/*
/hello/:id/world
/hello/worldThe second way includes a route param (more on that later).
Unlike Express, here the order of configuring routes don't matter when it comes to selecting the route based on the HTTP request path. It will always be the same, given the same routes are configured.
Assuming the above routes are configured for GET request and the client is making requests using GET method, the following will be routes matched for requests according to priority:
| request path | route |
|---|---|
| /hello/world | /hello/world |
| /hello//world | /hello/world |
| /hello/abcd/world | /hello/:id/world |
| /hello/abcde | /hello/* |
| /hello/abcd/efgh | /hello/* |
| /hell/abcd | 404 |
Route params are variable content in the array that usually provide information about the resource to make requests for, but are not part of the URL queries.
eg. /user/resyfer/profile has a variable part resyfer which refers to the username
of the user who's profile I want to view.
In libexpress, writing such route params as /hello/:username/profile can help you
extract the username value that the client gives, inside a middleware or controller
like:
char *username = get_req_param(req, "username");Query params point to the same path, but add additional info on the resource they
are fetching eg. www.youtube.com/watch?v=dQw4w9WgXcQ has path /watch but
specifies the ID of the video dQw4w9WgXcQ as query param. The query param
v has the value dQw4w9WgXcQ.
Multiple query params are added like /path?hello=world&foo=bar for which foo
has the value bar and hello has the value world.
You can fetch the value of a query param using:
char* value = get_req_query(req, "query");Coming Soon: The ability to iterate over params.
Beside the above, you can extract more information from the HTTP request:
char *body = get_req_body(req);
char *value = get_req_header(req, "header");
char *method = get_req_method(req);
char *path = get_req_path(req);You need to prepare your response before it is sent. The following way will ensure it.
set_res_body(res, body); // body is char*
set_res_header(res, header, value); // header, value are char*
char* header = get_res_header(res, header);After that you need to send a response before the controller reaches the end:
res_send(res);
// try to
// return res_send(res);
// so that execution will not happen below it