We will make a web application that allows you to quickly create subscribers and send emails to your subscribers in a simple way. We’ll use refine to develop the frontend easily and strapi for backend solutions.
Let's start by creating our strapi and refine projects.
Creating API with Strapi
npx create-strapi-app strapi-email-subscription-api --quickstart
After the project is loaded, the admin panel will open automatically open in the browser. We need to create an admin user in order to log in to the strapi.
With the information we will create here, we can now enter the strapi and start shaping our backend.
After logging into the Strapi interface, we have two collection models that we need to create for our e-mail subscription project.
We will create these collections from the Collection-Types Builder part of the strapi.
Subscribers
name
text field
Messages
subject
text fieldtext
text field
With these collections and features we have created, we can now create subscribers, delete them and make changes to them.
Creating panel with refine
Now let's refine the subscription panel. With superplate, we can quickly create a refine project
npx superplate-cli email-subscription-panel
Select the following options to complete the CLI wizard:
? Select your project type:
> refine
? Package manager:
> Npm
? Do you want to customize the theme?:
> No (Ant Design default theme)
? Data Provider:
> Strapi
? Do you want to customize layout?:
> Yes, I want
? i18n - Internationalization:
> No
After the upload is finished, let's go into our project and see how it looks.
cd email-subscription-panel
npm run dev
This is a example Refine project:
Let's list our messages and subscribers with refine. Here are the changes we need to make:
- Change Strapi API URL from refine
- Adding resources according to the collection name we created in Strapi
/App.tsx
import { Refine, Resource } from "@pankod/refine";
import "@pankod/refine/dist/styles.min.css";
import { DataProvider } from "@pankod/refine-strapi";
import strapiAuthProvider from "authProvider";
import { Header, Layout, OffLayoutArea } from "components";
function App() {
- const API_URL = "your-strapi-api-url";
+ const API_URL = "http://localhost:1337";
const { authProvider, axiosInstance } = strapiAuthProvider(API_URL);
const dataProvider = DataProvider(API_URL, axiosInstance);
return (
<Refine
dataProvider={dataProvider}
authProvider={authProvider}
Header={Header}
Layout={Layout}
OffLayoutArea={OffLayoutArea}
>
<Resource
name="subscribers"/>
<Resource
name="messages"/>
</Refine>
);
}
export default App;
After adding the resources, we need to define a user in the strapi in order to be able to login to the refine.
Let's login with this user we created
We can now list subscribers and messages and make changes to our list. Before doing this, let's create test users and messages on the strapi side.
Create SubscriberList.tsx and MessagesList.tsx file under the pages folder. Then, let's create our component as follows with the components and hooks that come with refine.
/src/pages/subscriber/SubscriberList.tsx
import React from "react";
import {
useTable,
List,
Table,
DateField,
DeleteButton,
IResourceComponentsProps,
} from "@pankod/refine";
import { ISubscriber } from "interfaces";
export const SubscriberList: React.FC<IResourceComponentsProps> = () => {
const { tableProps } = useTable<ISubscriber>();
return (
<List>
<Table {...tableProps} rowKey="id">
<Table.Column dataIndex="id" title="Id" />
<Table.Column dataIndex="name" title="Name" />
<Table.Column dataIndex="email" title="E-mail" />
<Table.Column
dataIndex="created_at"
title="createdAt"
render={(value) => <DateField format="LLL" value={value} />}
/>
<Table.Column<ISubscriber>
title="Unsubscribe"
dataIndex="actions"
render={(_, record): React.ReactNode => {
return (
<DeleteButton size="small" recordItemId={record.id} hideText />
);
}}
/>
</Table>
</List>
);
};
/src/pages/mail/MessageList.tsx
import React from "react";
import {
useTable,
List,
Table,
DateField,
IResourceComponentsProps,
} from "@pankod/refine";
import { IMail } from "interfaces";
export const MessageList: React.FC<IResourceComponentsProps> = () => {
const { tableProps } = useTable<IMail>();
return (
<List>
<Table {...tableProps} rowKey="id">
<Table.Column dataIndex="id" title="Id" />
<Table.Column dataIndex="subject" title="Subject" />
<Table.Column dataIndex="text" title="Body" />
<Table.Column
dataIndex="created_at"
title="createdAt"
render={(value) => <DateField format="LLL" value={value} />}
/>
</Table>
</List>
);
};
/src/interfaces/intex.d.ts
export interface ISubscriber {
id: any;
name: string;
email: string;
created_at: string;
}
export interface IMail {
subject: string;
text: string;
to: string;
create_at: string;
}
In this component:
We used refine's list and table to show our subscribers and messages.
Now let's see how our subscriber panel looks like:
Subscriber:
Messages:
As you can see, we were able to list our subscribers and e-mails very simply with refine. Now let's examine how we can create subscribers and messages from our interface.
/src/pages/subscriber/create.tsx
import {
Create,
Form,
Input,
useForm,
IResourceComponentsProps,
} from "@pankod/refine";
import { ICreateSubscriber } from "interfaces";
export const CreateSubscriber: React.FC<IResourceComponentsProps> = () => {
const { formProps, saveButtonProps } = useForm<ICreateSubscriber>();
return (
<Create saveButtonProps={saveButtonProps}>
<Form {...formProps} layout="vertical">
<Form.Item label="Name" name="name">
<Input />
</Form.Item>
<Form.Item
label="E-mail"
name="email"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
</Form>
</Create>
);
};
/src/pages/mail/create.tsx
import React, { useState } from "react";
import {
Create,
Form,
Input,
useForm,
IResourceComponentsProps,
} from "@pankod/refine";
import ReactMarkdown from "react-markdown";
import ReactMde from "react-mde";
import "react-mde/lib/styles/css/react-mde-all.css";
import { IMail } from "interfaces";
export const MailCreate: React.FC<IResourceComponentsProps> = () => {
const { formProps, saveButtonProps } = useForm<IMail>();
const [selectedTab, setSelectedTab] = useState<"write" | "preview">("write");
return (
<Create saveButtonProps={saveButtonProps}>
{console.log("create")}
<Form {...formProps} layout="vertical">
<Form.Item
label="Subject"
name="subject"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="Body"
name="text"
rules={[
{
required: true,
},
]}
>
<ReactMde
selectedTab={selectedTab}
onTabChange={setSelectedTab}
generateMarkdownPreview={(markdown: any) =>
Promise.resolve(<ReactMarkdown>{markdown}</ReactMarkdown>)
}
/>
</Form.Item>
<Form.Item
label="To"
name="to"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
</Form>
</Create>
);
};
Using refine's form and create components, we can now create subscribers and messages with refine.
We're done with our panel. We can now list, create and delete subscribers. Finally, the step of sending real mails using our panel and strapi is left. Let's see how we do this.
Strapi E-mail Plugin
In order to send mail via Strapi, we need to install the strapi-email plugin in our api project that we created above.
Let's open our API project that we created and download the email plugin.
cd strapi-email-subscription-api
npm install strapi-provider-email-sendgrid --save
After installing your plugin you will need to add some settings in config/plugins.js. If this file doesn't exists, you'll need to create it.
Configure your provider
Path — ./config/plugins.js
module.exports = ({ env }) => ({
email: {
provider: 'sendgrid',
providerOptions: {
apiKey: env('SENDGRID_API_KEY'),
},
settings: {
defaultFrom: 'your-email-adress',
defaultReplyTo: 'your-email-adress',
testAddress: 'your-email-adress',
},
},
});
💡 TIP: Strapi sends emails via sendgrid. That's why you need to create a SendGrid account and get an api-key.
Now, let's send the text and subject in the collection of messages we created over the strapi as parameters to the send() function of the email plugin.
api/messages/controllers/messages.js
const { parseMultipartData, sanitizeEntity } = require("strapi-utils");
module.exports = {
async create(ctx) {
let entity;
if (ctx.is("multipart")) {
const { data, files } = parseMultipartData(ctx);
entity = await strapi.services.messages.create(data, { files });
} else {
entity = await strapi.services.messages.create(ctx.request.body);
}
entity = sanitizeEntity(entity, { model: strapi.models.messages });
const { subject, text } = entity;
const worker = (await strapi.services.subscribers.find()).map(
(subscriber) => {
let to = subscriber.email;
return strapi.plugins["email"].services.email.send({
subject,
text,
to,
});
}
);
await Promise.all(worker);
return entity;
},
};
Our project is finished. Let's try it now.
Let's send the same e-mail to our subscribers shown in the picture at once.
Sending mail was successful. As you can see, we were able to send the same email to all subscribers by sending a single email.
For more information about Refine: https://refine.dev/