Server side vs Client Side for private web apps

Hey guys, one question, i’m building an app for accountants with a lot of infos about companies, not very private or whatever but it’s a lot of data. And i have to let them add, search, modify these infos. And it seems if i call the server for this services it will take time (like 2s) but on client its very fast. The app is with auth (email, password, 2auth) and i give them the access after they sign up, it’s not automated.

The q: If i move my code to the client, it can be dangerous? Like searching and other stuff, in terms of security. I need speed. I’m on free plan for now…until my app is ready, i guess when i buy the personal plan (or the business plan if it goes well) the speed will be the same right?

Thanks guys!

The database is on the server, not on the client.

So, you either pass the whole database to the client, maybe as a list of dictionaries and then do the searches on the client, maybe with list comprehensions, or you need to do a server call every time you need to search.

The most common way is to make a server call every time you do a search. Two seconds per call sounds a little slow, unless you are at the antipodes of London. Perhaps there is room for improvement when you make that call.

1 Like

Anything on the client can be seen by a suitably motivated person. Never send anything to the client you don’t want the user to see.

As Stefano says, it’s likely that you have some room for optimization of your current app. There are a lot of areas where an Anvil app can be optimized, and there are a lot of topics on the forum about optimization if you go searching. Or you can post some sample code that’s slow and ask others here to give you advice on optimizing it.

2 Likes

Here is the code:

Client side:
lista_informatii_companie = anvil.server.call(‘SEARCH_informatii_companie’, self.firma_drop_down.selected_value[“company_name”])

Server side:
@anvil.server.callable
def SEARCH_informatii_companie(company_name):
delete_selected_row = getattr(app_tables, get_user_email()).get(company_name=q.ilike(company_name))
return delete_selected_row

Can i do something different?

Can i make this better?

@anvil.server.callable(require_user=True)
def IMPORT_files_analiza_lunara(file_object):
df = pd.read_excel(file_object.get_bytes(), converters={‘cui’: str}).fillna(“-”) # converters={‘A’: str} this is for the 534534.0 problem
for i in range(len(df)):
getattr(app_tables, get_user_email_server()).add_row(company_name=df.loc[i, “company_name”],
cui=str(df.loc[i, “cui”]),
email=df.loc[i, “email”],
trezorerie=df.loc[i, “trezorerie”],
cont_cam_rc_436=df.loc[i, “cont_cam_rc_436”],
buget_stat=df.loc[i, “buget_stat”],
cont_cam_construc_rc_436=df.loc[i, “cont_cam_construc_rc_436”],
tva=df.loc[i, “tva”])

Thanks you!

When you post code on the forum, you really need to format it as code so the indentation is maintained. Otherwise it’s almost impossible to read and offer advice. This post shows how to format Python code for posting: How to ask a good question

1 Like
```python
@anvil.server.callable(require_user=True)
    def IMPORT_files_analiza_lunara(file_object):
      df = pd.read_excel(file_object.get_bytes(), converters={'cui': str}).fillna("-")
      for i in range(len(df)):
         getattr(app_tables, get_user_email_server()).add_row(company_name=df.loc[i, "company_name"],
                                                        cui=str(df.loc[i, "cui"]), 
                                                        email=df.loc[i, "email"], 
                                                        trezorerie=df.loc[i, "trezorerie"], 
                                                        cont_cam_rc_436=df.loc[i, "cont_cam_rc_436"], 
                                                        buget_stat=df.loc[i, "buget_stat"], 
                                                        cont_cam_construc_rc_436=df.loc[i, "cont_cam_construc_rc_436"],
                                                        tva=df.loc[i, "tva"])
```

Sorry, thanks for the advice! I hope is more clear now…

At a glance, I see a function called by the client that takes an Excel file object as an argument, reads it with pandas, and adds one row to a table for each row in the Excel file.

It is possible that a good part of the 2 seconds (if this is what we are still talking about) are spent importing pandas.

Anvil loads the whole app and imports all the server modules every time a server function is called, unless you are using the persistent server option (not available on all plans). I haven’t tested it, because I have a plan with persistent server, so I don’t care (much) about package import times, but chances are that pandas is a large package and takes some time to import.

Some time could be spent also adding the rows, if there are many rows in the file. Make sure you have accelerated tables enabled in the app settings, so adding rows will be faster. Also, try to wrap all the row-adding cycle in a transaction (or simply add the transaction decorator to the function), so everything will happen in one single transaction, rather than creating a transaction per row.

Some tips for troubleshooting the speed:

  • Try to create an http endpoint that does nothing in a new app. This will give you the baseline.
  • Try to add the import of pandas to the new app. This will tell you if pandas is slow to import.
  • Try with Excel files with 1, 10 or 100 rows. This will tell you about the impact of adding many rows in the same call.
  • Try with and without transaction.
2 Likes