An opinionated guide to configuring and launching an API Server — Perfect
There are many ways to configure and launch a Perfect Server. This guide presents opinionated best practices to configure a Perfect server with sessions, routes, filters, and logging.
Session
If we plan to have sessions on our Server Side Swift we need to configure it before we launch our server. Here is my example configuration that can be customized to your needs:
Also we need to call let sessionDriver = SessionMySQLDriver()
to configure the session filters and using purgeInterval to purge stalled sessions.
CORS
To make your APIs work with Single Page Apps (react, vue.js or Angular) we need to configure Cross-Origin Reference Sharing (CORS). You can read about CORS here:
The following is a working example of CORS configuration. We can call CORS.configure()
in the main before launching the server.
Make sure that you define all the custom http headers and acceptable host names otherwise web clients will not be accepted to consume your apis! 👈
CSRF
Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the attacker has no way to see the response to the forged request.
You can read about CSRF here:
To avoid CSRF attacks we need to configure our server. The following is an example that can be customized as you may need. We can call CSRF.configure()
in the main before launching the server.
Schema initialization
As we need to have sessions and database connections we need to configure them before we launch our server. We can place all configurations for database, SMTP, session,… in a JSON
and use it to configure our server. We can have configurations for different environments. The following example shows how we can configure our servers for macOS and Linux differently:
A typical configuration file looks like this:
Then we can set DB, session and SMPT configurations like the following:
let opts = initSchema(fname)
This also return baseURL
, httPort
,… that we will be using when we launch our server.
Logging
We typically need two different types of logs:
- Request logs that are brief and provide info about all the requests coming to our server. This can be done using a filter that we will be talking about in filters section.
- Logging for debug, error, and info cases.
To enable both types of logging we need the following:
RequestLogFile.location = opts["requestLoggingFileLocation"] as? String ?? ""
LogFile.location = opts["logFileLocation"] as? String ?? ""
Routes
There are many ways to define routes that are covered here:
Filters
There are two types of filters:
- Request — These can be used to check for session, authorization, or to use the scratchPad to carry over data between request and response as well as initiating a specific task or creating a pipeline.
- Response — These types of filters can be used to filter or modify the responses (e.g. modifying headers, content compression,…) as well as initiating a specific task (e.g. logging responses of APIs)
Request filters
The following presents an example of request filters:
Response filters
The following presents an example of response filters:
Launching server
Finally, we can launch our server as the following: