Let's start by defining a few endpoints for our service. We'll be implementing the logic for these endpoints later; for now, we'll ensure the following routes are present in the API and return 200 OK responses.
User Routes
For the purpose of these routes, you can assume the user's handle looks like @userName
. Don't worry about encoding/decoding the @
symbol in the URL. You can assume that the handle is unique for each user and is a valid URL path segment.
POST /api/user/:handle
- Creates a new userGET /api/user/:handle/posts
- Returns all posts of a user by their handle, ordered by time
Post Routes
POST /api/posts
- Creates a new postGET /api/posts/:id
- Returns a post by a text-based ID
Follower Routes
POST /api/follow/:handle
- Follows a user by their handle
Feed Routes
GET /api/feed/:handle
- Returns all posts of the people the user is following, ordered by time.
Workflow tip
The new namespace
directive can be used to define a namespace prefix for a group of related functions. Add the following line to your scratch file to help keep your code organized and easier to read.
namespace exercises.ex3_microblog
Routes library basics
We'll be using the Routes library to define our endpoints.
There are two primary functions for defining a service path:
Route.noCapture
- if your route does not capture any path parametersRoute.route
- if your route extracts a path segment for use as a variable
The Routes library provides a few functions, such as ok.text
or ok.json
, that help build HTTP responses.
Combining the two—path definition and HTTP response construction—will create a complete endpoint:
api.createPost : '{Route} ()
api.createPost = do
use Parser / s
Route.noCapture POST (s "api" / s "posts")
ok.text "ok"
The s "api" / s "posts"
section of the route definition above sets up a series of path segments that match the /api/posts
path.
Capturing a path segment would look like s "api" / s "posts" / Parser.text
, where Parser.text
represents the text of the path variable:
api.getPostById : '{Route} ()
api.getPostById = do
use Parser / s
postId =
PostId
(Route.route
GET (s "api" / s "posts" / Parser.text))
ok.text "ok"
We recommend defining one function per route, and then combining them all together with Route.<|>
in a final call to Route.run
. Route.run
produces a function that takes an HttpRequest
and returns an HttpResponse
, which is the expected type for deploying an HTTP service.
Route.run : '{g, Route, Exception} ()
-> HttpRequest
->{g, Exception} HttpResponse
📚 Instructions
Define the remaining endpoints by having them return a 200 OK response for each route. The response body does not matter for now; you can return ok.text "ok"
for each route.
We've provided two route stubs to use as a reference for this part of the exercise:
api.getPostById
- "GET /api/posts/:id"api.createPost
- "POST /api/posts"
The remaining routes are to implement are:
POST /api/user/:handle
GET /api/user/:handle/posts
POST /api/follow/:handle
GET /api/feed/:handle
Deploy all your routes as an HTTP service inside the provided ex3_microblog.deploy
function. It should return the URI where the service is deployed.
cloud-start/main> edit ex3_microblog.deploy
When you've implemented all the routes, update
your codebase and run your solution.
cloud-start/main> run submit.ex3_microblog.pt1
Solution
In addition to the provided api.getPostById
and api.createPost
functions, you should define the following routes:
namespace exercises.ex3_microblog
api.createUser : '{Route} ()
api.createUser = do
userHandle = Route.route POST (s "api" / s "user" / Parser.text)
ok.text "ok"
api.getUserPosts : '{Route} ()
api.getUserPosts = do
userHandle = Route.route GET (s "api" / s "user" / Parser.text / s "posts")
ok.text "ok"
api.followUser : '{Route} ()
api.followUser = do
userHandle = Route.route POST (s "api" / s "follow" / Parser.text)
ok.text "ok"
api.getFeed : '{Route} ()
api.getFeed = dos
userHandle = Route.route GET (s "api" / s "feed" / Parser.text)
ok.text "ok"
Combine all the routes with Route.run
to create the HTTP service:
api : HttpRequest ->{Exception} HttpResponse
api =
use Route <|>
Route.run
(api.createPost
<|> api.getPostById
<|> api.getUserPosts
<|> api.createUser
<|> api.followUser
<|> api.getFeed )
Deploy the service in the ex3_microblog.deploy
function:
deploy : '{IO, Exception} URI
deploy = Cloud.main do
hash = deployHttp exercises.env() api
serviceName = ServiceName.create "microblog"
ServiceName.assign serviceName hash