Let's set up the service to return JSON responses. We'll need to define the data types for our posts, users, and followers, and then write functions to serialize and deserialize these data types to and from JSON.
Using the JSON library
You may want to define the following data types for your service:
type client.User =
type client.CreatePost =
type client.Post =
type client.Following =
These "client-facing" models might have more or less information than what you'll eventually need in the database, so while it's possible to directly save the client-facing models in Cloud storage, it's a good practice to decouple layers of the application which contain different data or evolve at different rates.
Once you have your target data types, you'll need to use the Json library to serialize and deserialize them. Here's an example of turning a Unison data type into a JSON string:
User.toJson : client.User -> Json
User.toJson = cases
client.User.User userId userHandle name avatar ->
use Json text
use List ++
use Optional fold
maybeAvatar = fold '[] (uri -> [("avatar", text uri)]) avatar
maybeUserId = fold '[] (uid -> [("userid", text uid)]) userId
jsonFields =
[("userHandle", text userHandle), ("name", text name)]
Object (jsonFields ++ maybeAvatar ++ maybeUserId)
Turning a Unison datatype into a JSON object typically takes the form of a pattern match on the data type, where the fields of the data type are translated into JSON using constructors like Json.object
or Json.text
.
Here's an example of turning a JSON object into a Unison data type:
User.fromJson : '{Decoder} client.User
User.fromJson = do
use Decoder text
use object at at!
userId = optional! (at "userId" text)
userHandle = at! "userHandle" text
name = at! "name" text
avatar = optional! (at "avatar" text)
client.User.User userId userHandle name avatar
We represent JSON deserialization with the Decoder
ability. You can inspect keys with the object.at
function and decode them into the appropriate type with functions like Decoder.text
or Decoder.int
.
📝 Instructions
Update your service to expect and produce the following JSON data blobs. You can supply stub values for the JSON responses for now. Take a look at the examples below to get a sense of the expected types for the required keys.
When you're ready, update
your codebase and run your solution.
cloud-start/main> run submit.ex3_microblog.pt2
You can name your data types and add fields as you see fit, but they should conform to the JSON below at minimum:
User endpoints
POST /api/user
{
"userHandle": "@bob",
"name": "Bob Bob",
"avatar": "https://example.com/avatar.jpg"
}
Where the "avatar" field is optional.
The response from the POST should be a 200 OK with a JSON body with the following form:
{
"userId": "123uuidUserId123"
}
GET /api/user/:id
The response should be a 200 OK with a JSON body with the following form:
{
"userId": "123uuidUserId123",
"userHandle": "@bob",
"name": "Bob Bob",
"avatar": "https://example.com/avatar.jpg"
}
Post endpoints
POST /api/posts
Expect a JSON body with the following form:
{
"body": "Text",
"userId": "UserId"
}
The response should be a 200 OK with a JSON body with the following form:
{
"postId": "123uuidPostId123"
}
GET /api/posts/:id
The response should be a 200 OK with a JSON body with the following form:
{
"body": "Text",
"userName": "Bob Bob",
"userHandle": "@bob",
"timestamp": "2021-01-01T00:00:00Z"
}
Follower endpoints
POST /api/follow
The endpoint should contain a JSON request body with the following:
{
"follower": "123uuidUserId123",
"target": "123uuidUserId123"
}
The response body should be a 200 OK with JSON which looks like:
{
"followingId": "123uuidFollowingId123"
}
DELETE /api/follow/:handle
This endpoint does not expect a request body. The returned JSON response should look like:
{
"followingId" : "123uuidFollowingId123"
}
User feed endpoint
GET /api/feed/:handle
Returns all posts of the users you're following, ordered by time.
The response should be a JSON array:
[
{
"body": "Text",
"userName": "Bob Bob",
"userHandle": "@bob",
"timestamp": "2021-01-01T00:00:00Z"
}
]