Luna is a Golang-based framework that leverages the Echo web framework for server-side rendering (SSR) of React applications. It provides a seamless, full-stack development experience for building dynamic and performant web applications, making it ideal for full-stack developers looking to integrate the power of Golang on the backend with the flexibility of React on the frontend.
Warning
This framework is beta quality. Expect breaking changes and bugs
Check this link for an example production: Demo
- Full backend api support with Echo
- SSR with React
- Hot reloading development
- Route based caching
- Custom Middleware support
Ensure you have Go and Yarn or npm installed on your system.
go get github.com/Djancyp/luna@v1.0.0
func main() {
app, err := luna.New(luna.Config{
ENV: ENV,
RootPath: "frontend/",
PublicPath: "frontend/public",
AssetsPath: "frontend/src/assets",
ServerEntryPoint: "frontend/src/entry-server.tsx",
ClientEntryPoint: "frontend/src/entry-client.tsx",
TailwindCSS: true,
FaviconPath: "favicon.svg",
HotReloadServerPort: 3000,
Store: store.ReturnStore,
Head: pkg.MainHead{
Attributes: []string{
`<link rel="manifest" href="/manifest.json" />`,
},
},
Routes: []pkg.ReactRoute{
{
Path: "/",
Head: pkg.Head{
Title: "mi-Deck",
Description: "Home page",
CssLinks: googleFont,
},
Middleware: []echo.MiddlewareFunc{
middlewares.UserLoggedin,
},
CacheExpiry: time.Now().Add(365 * 24 * time.Hour).Unix(),
},
{
Path: "/dash",
Head: pkg.Head{
Title: "mi-deck - Dash",
Description: "Dashboard page",
CssLinks: googleFont,
},
Props: props.ReturnDashProps,
Middleware: []echo.MiddlewareFunc{
middlewares.RequireLogin,
},
},
{
Path: "/decks",
Head: pkg.Head{
Title: "mi-deck - Decks",
Description: "Decks page",
CssLinks: googleFont,
},
Props: props.ReturnDeckProps,
Middleware: []echo.MiddlewareFunc{
middlewares.RequireLogin,
},
},
{
Path: "/decks/new",
Head: pkg.Head{
Title: "mi-deck - New Deck",
Description: "New deck page",
CssLinks: googleFont,
},
Middleware: []echo.MiddlewareFunc{
middlewares.RequireLogin,
},
CacheExpiry: time.Now().Add(365 * 24 * time.Hour).Unix(),
},
{
Path: "/decks/edit/:id",
Head: pkg.Head{
Title: "mi-deck - Edit Deck",
Description: "Edit deck page",
CssLinks: googleFont,
},
Props: props.ReturnEditDeckProps,
Middleware: []echo.MiddlewareFunc{
middlewares.RequireLogin,
},
},
{
Path: "/decks/:id/play",
Head: pkg.Head{
Title: "mi-deck - Edit Deck",
Description: "Edit deck page",
CssLinks: googleFont,
},
Props: props.ReturnPlayDeckProps,
Middleware: []echo.MiddlewareFunc{
middlewares.RequireLogin,
},
},
},
})
// make sure you handle the error
app.Start(":8080")
}
Every page has access to this on the frontend. You can use create a provider for this or direct access on your components by
const { store } = store;
func ReturnStore(c echo.Context) map[string]interface{} {
return map[string]interface{}{
"User": returnUser(c),
}
}
func returnUser(c echo.Context) map[string]interface{} {
session, err := gothic.Store.Get(c.Request(), "userSession")
if err != nil {
return map[string]interface{}{
"loggedIn": false,
}
}
if _, ok := session.Values["id"]; !ok {
return map[string]interface{}{
"loggedIn": false,
}
}
return map[string]interface{}{
"loggedIn": true,
"id": session.Values["id"],
"email": session.Values["email"],
"name": session.Values["name"],
"avatar": session.Values["picture"],
}
}
Props are page level data that can be passed. You can Access the data on the frontend by using the store object
const { lates, totals, analytics } = props;
func ReturnDashProps(c echo.Context, params map[string]string) map[string]interface{} {
// u can use params to get url params
// paramId := params["id"]
// get session
props := map[string]interface{}{}
session, err := gothic.Store.Get(c.Request(), "userSession")
if err != nil {
return props
}
// Check if the user ID is set in the session
if _, ok := session.Values["id"]; !ok {
return props
}
id := session.Values["id"].(string)
lates := getLatest(id)
totals := getTotals(id)
analytics := getAnalitics(id)
return map[string]interface{}{
"lates": lates,
"totals": totals,
"analytics": analytics,
}
}
func getLatest(id string) []database.GetDecksByUserIDRow {
data := []database.GetDecksByUserIDRow{}
ctx := context.Background()
db := database.New(database.DB)
body := database.GetDecksByUserIDParams{
UserID: id,
Limit: 2,
Offset: 0,
}
decks, err := db.GetDecksByUserID(ctx, body)
if err != nil && sql.ErrNoRows != err {
return data
}
if sql.ErrNoRows == err {
return []database.GetDecksByUserIDRow{}
}
if decks == nil {
return []database.GetDecksByUserIDRow{}
}
return decks
}
type Totals struct {
Decks int64
Cards int
}
func getTotals(id string) Totals {
data := Totals{
Decks: 0,
Cards: 0,
}
if id == "" {
return data
}
ctx := context.Background()
db := database.New(database.DB)
deckTotal, err := db.CountDecksByUserID(ctx, id)
if err != nil && sql.ErrNoRows != err {
return data
}
cardTotal, err := db.CountCardsByUserID(ctx, id)
if err != nil && sql.ErrNoRows != err {
return data
}
data.Cards = int(cardTotal)
data.Decks = deckTotal
return data
}
func getAnalitics(id string) []database.MonthlyUserProgress {
data := []database.MonthlyUserProgress{}
if id == "" {
return data
}
ctx := context.Background()
db := database.New(database.DB)
analitics, err := db.GetMonthlyUserProgress(ctx, id)
if err != nil && sql.ErrNoRows != err {
return data
}
if sql.ErrNoRows == err {
return []database.MonthlyUserProgress{}
}
if analitics == nil {
return []database.MonthlyUserProgress{}
}
return analitics
}
Check this link for an example project: Example
Thank you for considering contributing to this project! Contributions are what make the open-source community such a valuable place to learn, inspire, and create. Any contributions you make are greatly appreciated.
To contribute:
- Fork the repository.
- Create a branch for your feature (
git checkout -b feature/NewFeature
). - Commit your changes (
git commit -m 'Add some NewFeature'
). - Push to the branch (
git push origin feature/NewFeature
). - Open a pull request.
Please make sure to update tests as appropriate and adhere to the code of conduct in all interactions.
This project is licensed under the MIT License. See the LICENSE file for more information.