ICP Stories is a simple story sharing platform built on the Internet Computer. It allows users to create, read, update, and delete stories. Users can create one or more blogs and share their stories with the world.
-
Install
nvm
:curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
-
Switch to node v20:
nvm install 20
nvm use 20
-
Install build dependencies:
For Ubuntu and WSL2
sudo apt-get install podman
For macOS
xcode-select --install brew install podman
-
Install
dfx
DFX_VERSION=0.16.1 sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)"
-
Add
dfx
to PATH:echo 'export PATH="$PATH:$HOME/bin"' >> "$HOME/.bashrc"
-
Clone the repository:
git clone
-
Run a local replica
dfx start --host 127.0.0.1:8000
If you make any changes to the
StableBTreeMap
structure like change datatypes for keys or values, changing size of the key or value, you need to restartdfx
with the--clean
flag.StableBTreeMap
is immutable and any changes to it's configuration after it's been initialized are not supported.dfx start --host 127.0.0.1:8000 --clean
-
Deploy the canister
dfx deploy
Also, if you are building an HTTP-based canister and would like your canister to autoreload on file changes (DO NOT deploy to mainnet with autoreload enabled):
AZLE_AUTORELOAD=true dfx deploy
-
Stop a local replica
dfx stop
When a canister is deployed, dfx deploy
produces a link to the Candid interface in the shell output.
Candid interface provides a simple UI where you can interact with functions in the canister.
On the other hand, you can interact with the canister using dfx
via CLI:
dfx canister id <CANISTER_NAME>
Example:
$ dfx canister id icp_stories
bkyz2-fmaaa-aaaaa-qaaaq-cai
Now, the URL of your canister should like this:
http://bkyz2-fmaaa-aaaaa-qaaaq-cai.localhost:8000
With this URL, you can interact with the canister using an HTTP client of your choice. We are going to use curl
.
Then using Postman or curl, you can interact with the canister.
These are the types used in the canister.
class Member {
id: string;
username: string;
name: string;
tagline?: string | null;
bio?: string | null;
image?: string | null;
genres?: string[];
createdAt: Date;
updatedAt?: Date | null;
blogs: string[];
}
This is the type of a member. A member can have one or more blogs.
class Blog {
id: string;
userId: string;
name: string;
about: string;
socials: Record<string, string>;
series: string[];
posts: string[];
createdAt: Date;
updatedAt: Date | null;
}
This is the type of a blog. A blog can have one or more posts. A blog can also have one or more series. Series are a collection of posts.
class Series {
id: string;
name: string;
userId: string;
blogId: string;
description: string;
cover_image?: string | null;
genres?: string[];
sorting: SeriesSorting;
posts: string[];
createdAt: Date;
updatedAt?: Date | null;
}
This is the type of a series. A series can have one or more posts.
class Post {
id: string;
userId: string;
blogId: string;
seriesId?: string | null;
status: PostStatus;
title: string;
slug: string;
subtitle?: string | null;
content: string;
genres?: string[];
cover_image?: string | null;
seo_title?: string | null;
seo_description?: string | null;
canonical_url?: string | null;
createdAt: Date;
updatedAt: Date | null;
comment_enabled: boolean;
}
class Comment {
id: string;
userId: string;
content: string;
createdAt: Date;
}
This is the type of a post and a comment. A post can have one or more comments.
These are the endpoints available in the canister.
Endpoint: POST /auth/register
Sample payload:
{
"username": "testing102",
"name": "john doe",
"tagline": "amazing developer"
}
Endpoint: GET /auth/account/:id
Endpoint: GET /auth/account/by-username/:username
Endpoint: PATCH /auth/account/:id
Sample payload:
{
"name": "john doe",
"tagline": "amazing developer",
"bio": "I am an amazing developer",
"genres": []
}
Endpoint: DELETE /auth/account/:id
Endpoint: GET /auth/account/list
Endpoint: POST /blogs/create/:userId
Sample payload:
{
"name": "Blockchainer",
"about": "amazing developer",
"socials": {
"twitter": "https://x.com/netrobeweb"
}
}
Endpoint: GET /blogs/:id
Endpoint: PATCH /blogs/:id
Sample payload:
{
"name": "Blockchainer - [UPDATED]",
"about": "amazing developer",
"socials": {
"twitter": "https://x.com/netrobeweb",
"github": "https://x.com/netrobeweb"
}
}
Endpoint: DELETE /blogs/:id
Endpoint: GET /blogs
Endpoint: GET /series
Endpoint: GET /series/list/user/:userId
Endpoint: GET /series/list/blog/:blogId
Endpoint: POST /series/create/:userId
Sample payload:
{
"blogId": "533e2a5a-a92e-41ad-a2b8-4054036e908e",
"name": "Creating an NFT Collection on ICP",
"description": "amazing developer",
"sorting": "oldest_first",
"genres": ["blockchain"],
"cover_image": null
}
Endpoint: GET /series/:id
Endpoint: PATCH /series/:id
Sample payload:
{
"name": "Creating an NFT Collection on ICP",
"description": "amazing developer",
"sorting": "oldest_first",
"genres": ["blockchain"],
"cover_image": null
}
Endpoint: DELETE /series/:id
Endpoint: GET /posts
Endpoint: GET /posts/user/:userId
Endpoint: GET /posts/blog/:blogId
Endpoint: GET /posts/series/:seriesId
Endpoint: POST /posts/create/:userId
Sample payload:
{
"blogId": "f9579672-c745-4256-89d1-11d1833b5dba",
"title": "Creating an NFT Collection on ICP",
"subtitle": "amazing developer",
"status": "draft",
"slug": "creating-an-nft-collection-on-icp",
"genres": ["blockchain"],
"content": "# Hi today we will learn about NFTs",
"comment_enabled": true
}
Endpoint: GET /posts/:id
Endpoint: PATCH /posts/:id
Sample payload:
{
"status": "published",
"comment_enabled": true
}
Endpoint: DELETE /posts/:id
Endpoint: POST /posts/:userId/comment
Sample payload:
{
"postId": "20883cb4-155f-437d-90c0-6001fd61f9fd",
"content": "# Hi today we will learn about NFTs"
}
Endpoint: DELETE /posts/:postId/comment/:commentId
- A user creates an account
- A user creates a blog
- A user creates a series
- A user creates a post
- A user creates a comment on a post